﻿using SBlogCore.Common.App;
using SBlogCore.Common.Attributes;
using SBlogCore.Common.RemoteRequest.Attributes;
using SBlogCore.Common.RemoteRequest.Attributes.Filters;
using SBlogCore.Common.RemoteRequest.Models;
using SBlogCore.Common.Utilities.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SBlogCore.Common.RemoteRequest
{
    [SkipScan]
    public static class HttpClientPartFactory
    {
        public static HttpClientPart Create(MethodInfo method, object?[]? args)
        {
            //判断方法是否标记为远程请求
            if (!method.IsDefined(typeof(HttpMethodBaseAttribute), true))
            {
                throw new Exception($"方法{method.Name}不是远程请求方法");
            }
            IEnumerable<MethodParameterInfo> parameters = Array.Empty<MethodParameterInfo>();
            //GetCustomAttributes(true),是否获取特性的上级
            HttpMethodBaseAttribute? httpMethodBase = method.GetCustomAttribute<HttpMethodBaseAttribute>(true);
            if (httpMethodBase == null)
            {
                throw new Exception("请通过Get/Post/Delete/Put等特性标记请求方式，以及请求路径！");
            }
            ClientAttribute? client = method.GetCustomAttribute<ClientAttribute>(true);
            if (client == null)
            {
                throw new Exception("请标通过Client特性标记请求服务器！");
            }
            if (args != null && args.Count() > 0)
            {
                // 解析方法参数及参数值
                parameters = method.GetParameters().Select((u, i) => new MethodParameterInfo(Parameter: u,Name:u.Name!,Value: args[i]!));
            }
            //获取方法所在的类
            Type declaringType = method.DeclaringType!;
            var httpClientFactory = AppBase.GetService<IHttpClientFactory>();
            HttpClientPart clientPart = new HttpClientPart(httpClientFactory);
            clientPart
                .SetRequestUri(httpMethodBase.RequestUrl)
                .SetClientName(client.Name)
                .SetMethod(httpMethodBase.Method)
                .SetHeaders(declaringType, method, parameters)
                .SetQueries(parameters)
                .SetBodyPart(parameters)
                .SetInterceptors(parameters);
            return clientPart;
        }
        /// <summary>
        /// 设置请求头
        /// </summary>
        /// <param name="clientPart"></param>
        /// <param name="declaringType">所在类</param>
        /// <param name="method">方法</param>
        /// <param name="parameters">参数</param>
        /// 查找所有Hearder特性
        private static HttpClientPart SetHeaders(this HttpClientPart clientPart, Type declaringType,MethodInfo method, IEnumerable<MethodParameterInfo> parameters)
        {
            //获取类上的Header特性
            Dictionary<string, object>? typeHeaderAttributes =
                (
                declaringType.IsDefined(typeof(HeadersAttribute), true) ? 
                declaringType.GetCustomAttributes<HeadersAttribute>() :
                Array.Empty<HeadersAttribute>()
                ).ToDictionary(u=>u.Key,u=>u.Value);
            //获取方法上的Header特性
            Dictionary<string, object>? methodHeaderAttributes =
                (
                method.IsDefined(typeof(HeadersAttribute), true) ?
                method.GetCustomAttributes<HeadersAttribute>() :
                Array.Empty<HeadersAttribute>()
                ).ToDictionary(u => u.Key, u => u.Value);
            //获取参数上的Header特性
            var headerParameters = parameters.Where(u => u.Parameter.IsDefined(typeof(HeadersAttribute), true));
            var parHeaderAttributes = new Dictionary<string, object>();
            foreach (var item in headerParameters)
            {
                var headersAttribute = item.Parameter.GetCustomAttribute<HeadersAttribute>(true);
                if (item.Value != null) parHeaderAttributes.Add(headersAttribute?.Key ?? item.Name, item.Value);
            }
            // 合并所有请求报文头
            var headers = typeHeaderAttributes.AddOrUpdate(methodHeaderAttributes).AddOrUpdate(parHeaderAttributes);
            return clientPart.SetHeaders(headers);
        }
        /// <summary>
        /// 设置Url地址栏参数
        /// </summary>
        /// <param name="clientPart"></param>
        /// <param name="parameters">参数</param>
        private static HttpClientPart SetQueries(this HttpClientPart clientPart, IEnumerable<MethodParameterInfo> parameters)
        {
            //除了Body标记、Header标记、委托以外的参数均可认为是Url参数
            var parameterlist = parameters.Where(w =>
            !w.Parameter.IsDefined(typeof(BodyAttribute), true)||
            !w.Parameter.IsDefined(typeof(HeadersAttribute), true) ||
            !w.Parameter.IsDefined(typeof(OnFilterBaseAttribute), true) ||
            !w.Parameter.ParameterType.IsAssignableTo(typeof(Delegate))
            ).ToList();
            Dictionary<string, object> queries = new Dictionary<string, object>();
            foreach (var item in parameterlist)
            {
                //如果用了QueryString标记
                if (item.Parameter.IsDefined(typeof(QueryStringAttribute), true))
                {
                    var queryStringAttribute = item.Parameter.GetCustomAttribute<QueryStringAttribute>();
                    queries.Add(queryStringAttribute?.Alias ?? item.Name, item.Value);
                }
                else {
                    queries.Add(item.Name, item.Value);
                }
            }
           return clientPart.SetQueries(queries);
        }
        /// <summary>
        /// 设置Body
        /// </summary>
        /// <param name="clientPart"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private static HttpClientPart SetBodyPart(this HttpClientPart clientPart, IEnumerable<MethodParameterInfo> parameters)
        {

            // 配置 Body 参数，只取第一个
            var bodyParameter = parameters.FirstOrDefault(u => u.Parameter.IsDefined(typeof(BodyAttribute), true));
            if (bodyParameter != null)
            {
                var bodyAttribute = bodyParameter.Parameter.GetCustomAttribute<BodyAttribute>(true);
                if (bodyAttribute!=null)
                {
                    clientPart
                        .SetBody(bodyParameter.Value)
                        .SetContentType(bodyAttribute.ContentType)
                        .SetEncoding(Encoding.GetEncoding(bodyAttribute.Encoding)); 
                }
            }
            return clientPart;
        }
        /// <summary>
        /// 设置请求拦截
        /// </summary>
        /// <param name="clientPart"></param>
        /// <param name="declareType"></param>
        /// <returns></returns>
        private static HttpClientPart SetInterceptors(this HttpClientPart clientPart, IEnumerable<MethodParameterInfo> parameters)
        {
            //只有打了标记才可以视为监听方法
            var parameterList = parameters.Where(w => w.Parameter.IsDefined(typeof(OnFilterBaseAttribute))&&w.Parameter.ParameterType.IsAssignableTo(typeof(Delegate)));
            var onHttpClientList = parameterList.Where(w => w.Parameter.IsDefined(typeof(OnHttpClientAttribute)));
            foreach (var item in onHttpClientList)
            {
                if (item.Value is Action<HttpClient> onHttpClient)
                {
                    clientPart.SetOnHttpClient(onHttpClient);
                }
            }
            var onRequestList = parameterList.Where(w => w.Parameter.IsDefined(typeof(OnRequestingAttribute)));
            foreach (var item in onHttpClientList)
            {
                if (item.Value is Action<HttpRequestMessage> onRequesting)
                {
                    clientPart.SetOnRequesting(onRequesting);
                }
            }
            var onResponsList = parameterList.Where(w => w.Parameter.IsDefined(typeof(OnResponsingAttribute)));
            foreach (var item in onHttpClientList)
            {
                if (item.Value is Action<HttpResponseMessage> onResponsing)
                {
                    clientPart.SetOnResponsing(onResponsing);
                }
            }
            var onExceptionList = parameterList.Where(w => w.Parameter.IsDefined(typeof(OnExceptionAttribute)));
            foreach (var item in onHttpClientList)
            {
                if (item.Value is Action<HttpResponseMessage> onException)
                {
                    clientPart.SetOnException(onException);
                }
            }
            return clientPart;
        }
    }
}
